/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package FEMSolver;

import BounderyPack.Boundery;
import BounderyPack.BounderyNodePair;
import BounderyPack.DBoundery;
import BounderyPack.DirihletPair;
import HashMatrix2.HashMatrix2;
import HashMatrix2.LUFactorization;
import MathLib.triangulation.SaveStructures.Node;
import MathLib.triangulation.SaveStructures.Triangle;
import MathLib.triangulation.SaveStructures.TriangleL;
import java.awt.Toolkit;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JProgressBar;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;

/**
 *
 * @author mark_o
 */
public class MyPenaltyIteration extends SwingWorker< Void,Void> {




    protected double h;
    protected double eps;
    protected double[] b;
    protected HashMatrix2 A;
    protected HashMatrix2 A2;
    JProgressBar pb;
    JTextArea Text;
    public String outPath;
    DirihletPair [] mas;

    protected ArrayList<DBoundery> NodeList;
    protected ArrayList<Boundery> NodeList2;
    protected ArrayList<Node> AllNodeList;
    protected ArrayList<Triangle> TriangleList;
    public double[] bLive;

    public MyPenaltyIteration(double h, double eps, double[] b, HashMatrix2 A, HashMatrix2 A2, JProgressBar pb, JTextArea Text, String outPath, DirihletPair[] mas, ArrayList<DBoundery> NodeList, ArrayList<Boundery> NodeList2, ArrayList<Node> AllNodeList, ArrayList<Triangle> TriangleList) {
        this.h = h;
        this.eps = eps;
        this.b = b;
        this.A = A;
        this.A2 = A2;
        this.pb = pb;
        this.Text = Text;
        this.outPath = outPath;
        this.mas = mas;
        this.NodeList = NodeList;
        this.NodeList2 = NodeList2;
        this.AllNodeList = AllNodeList;
        this.TriangleList = TriangleList;
    }

//    public MyPenaltyIteration(double h, double eps, double[] b, HashMatrix2 A, ArrayList<DBoundery> NodeList,JTextArea Text,DirihletPair [] mas) {
//        this.h = h;
//        this.eps = eps;
//        this.b = b;
//        this.A = A;
//        this.NodeList = NodeList;
//        this.Text = Text;
//        this.mas = mas;
//    }

    
    private double penalty(double ui,int i){
        for (int j = 0; j < NodeList.size(); j++) {
            DBoundery boundery = NodeList.get(j);
            if(boundery.contains(i)){
                //System.out.println("зайшли в штраф  " + Math.max((ui), h)+"\t\t"+(h-ui));
                //System.out.println("зайшли в штраф  " + (-Math.max((h-ui), 0)));
                return -Math.max((h-ui), 0);////////////////////////////////////////////////////////////////
            }
        }
        return 0;
    }

    private double[] penalty(double[] ui){
        double[] res = new double[ui.length];
        for (int index = 0; index < ui.length; index++) {
            res[index] = penalty(ui[index], index);
        }
        return res;
    }

    private boolean check(double[] u1,double[] u2){
        boolean key = true;
        for(int i=0;i<u1.length;i++){
            if(Math.abs(u1[i]-u2[i])>eps){
                key = false;
                System.out.println("===========================");
                System.out.print("перевірка:\t");
                System.out.println(i+"\t"+Math.abs(u1[i]-u2[i]));
                System.out.println("===========================");
                break;
            }
        }

        /*if(key){
        for(int i=0;i<u1.length;i++){
        if(Math.abs( penalty(u0[i], i))>eps){
        key = false;
        System.out.println(penalty(u0[i], i));
        break;
        }
        }
        }*/
        return key;
    }

    double[] u0;

    @Override
    protected  Void doInBackground(){



        /*for(int i = 0;i<A2.getN();i++){
        for(int j = 0;j<A2.getN();j++){
        System.out.print(A2.getValue(i, j)+" ");
        }
        System.out.println(";");
        }
        System.out.println("=====================================================");*/
        //u0 = new double[b.length];

        Text.append("\nПочаток обрахунку першого наближення");
        LUFactorization lu = new LUFactorization(A);



        //u0 = lu.calculate(b); //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        //u0=new double[]{3.125487127960415 ,      4.999998161215734 ,       4.999998027311023        ,3.0934429329551407        ,3.0940930175322783        ,3.0933828420436615        ,3.125418472883064        ,4.999997959804245    ,3.0548010506228045        ,3.064936716636373        ,3.0535106939217385     ,3.0768203732497836        ,3.100226062643091        ,3.1173834986235143        ,3.133635244572521        ,4.999997509605569        ,4.9999975811341475        ,4.999997677486435        ,3.1265553354814046        ,3.106640881465038        ,3.1244075471878383        ,3.1257212423816365       ,3.071537285248495        ,3.0734394100719795        ,4.999997073205902        ,3.082715036777281        ,3.048362166766605        ,4.999997070149889        ,3.049187435889443         ,3.1153197619496256        ,3.1048396867226047        ,3.1185286784020114        ,3.103906512201301        ,3.1203889262631357        ,3.1132000995157956        ,3.125087742118325        ,3.106407973709328        ,3.119161583596059        ,3.1205535194460303        ,3.1187843658597223        ,3.1247013562187003        ,3.120510735279798        ,3.1219285794065796        ,3.1228423774356555        ,3.090439653187103        ,3.10974796383968        ,4.999997702495683        ,4.9999983100840195        ,3.1061631279881325          ,3.0683244281835877        ,3.0890850685102285           ,3.0415375540189475        ,3.0446886153583432        ,3.0592112010154677        ,3.116290252708341        ,4.999998115917833        ,3.1328293068602466        ,4.9999980479169235        ,3.1247920580968955        ,3.116438945426476        ,3.1327149120884394         ,3.047248609742371           ,3.053779631895772        ,3.0620612785488084        ,3.065440994497524        ,3.098397638720138        ,4.999996966246385        ,3.06455336221905        ,3.0894096074267012        ,3.0438843826932227        ,4.999997069766711 };
        u0 = new double[A.getN()];
        for(int i = 0;i<A.getN();i++){
            u0[i] = h;
        }


        //System.out.println();
        //System.out.println("+++++++++++++++++перше набл++++++++++++++++++++++++++++++++++++++++");
        //for(int i=0;i<b.length;i++){
        //        System.out.println((u0[i]));
        //    }
        Text.append("\nКінець обрахунку першого наближення");

        System.out.println();
        //for(int i=0;i<b.length;i++){
            //u0[i] = h;
          //  A2.addValue(i, i, 1.0/eps);///////////////////////////
        //}
        //Text.append("\nПочаток LU-розкладу");
        //lu = new LUFactorization(A2);
        //Text.append("\nКінець LU-розкладу");

        Text.append("\nПочаток ітераційного процесу");
        double[] u1 = null;
        double[] B = new double[b.length];
        int iter = -1;
        while(true){
            iter++;
            Text.append("\nІтерація № "+iter);
            Text.setCaretPosition(Text.getText().length());
            //bLive = this.calc(penalty(u0));
            bLive = this.calc2(penalty(u0));
            for(int i=0;i<b.length;i++){
                B[i]= b[i] + bLive[i];///////////////////////////////////////
                //System.out.println(penalty(u0[i], i)/eps*bLive[i]);
                //System.out.println(bLive[i]);
            }
            u1 = lu.calculate(B);

            if(check(u1, u0)){
                Text.append("\n\n\n Кінець!!! ");
                Text.setCaretPosition(Text.getText().length());
                System.out.println("розвязок\n===================================================================");
                for(int i=0;i<b.length;i++){
                    System.out.println((u0[i]));
                }
                break;
                
            }
            if(iter==100){break;}

            u0 = u1;

            try {
                Thread.sleep(100);
            } catch (InterruptedException ignore) {}
        }

        PrintWriter pw =  null;
        try {
            pw = new PrintWriter(new BufferedOutputStream(new FileOutputStream(outPath)));
            pw.println(u1.length + mas.length);
        if(mas.length>0){
            int j;
            for (j = 0; j < mas[0].getNodeNumber(); j++) {
                pw.println(u1[j]);
            }

            for(int i=0; i<mas.length-1; i++){
                int k=0;
                pw.println(mas[i].getValue());
                while(mas[i+1].getNodeNumber() - mas[i].getNodeNumber()-k!=1){
                    pw.println(u1[j]);
                    j++;
                    k++;
                }
            }

            pw.println(mas[mas.length-1].getValue());

            for (int i = j; i < u1.length; i++) {
                pw.println(u1[i]);
            }
        }else{
            for (int i = 0; i < u1.length; i++) {
                pw.println(u1[i]);
            }
        }

        } catch (FileNotFoundException ex) {
            Logger.getLogger(MyPenaltyIteration.class.getName()).log(Level.SEVERE, null, ex);
        }finally{
            pw.close();
        }


        return null;
    }

    @Override
    protected void done() {
        Toolkit.getDefaultToolkit().beep();
        Text.append("\nКІНЕЦЬ!!!");
//        pb.setIndeterminate(false);
    }

    private double[] calc(double[] u0) {
        double[] bTmp = new double[u0.length];
        for (int i = 0; i < TriangleList.size(); i++) {
            Triangle triangle = TriangleList.get(i);
            double[] tmp =generateNapiv((TriangleL) triangle,u0);
            bTmp[triangle.A()-1]+=tmp[0];
            bTmp[triangle.B()-1]+=tmp[1];
            bTmp[triangle.C()-1]+=tmp[2];
        }
        return bTmp;
    }

    private double[] calc2(double[] u){
        double[] res = new double[b.length];
        for (int index = 0; index < NodeList2.size(); index++) {
            Boundery boundery = NodeList2.get(index);

            for (int iter = 0; iter < boundery.getNodeList().size(); iter++) {
                BounderyNodePair temp = boundery.getNodeList().get(iter);
                Node vA = temp.getA(),vB = temp.getB();
                double length = ( Math.sqrt( (vA.x - vB.x)*(vA.x - vB.x) + (vA.y - vB.y)*(vA.y - vB.y) ) );
                res[vA.Number]+=length*u[vA.Number]/2;
                res[vB.Number]+=length*u[vB.Number]/2;
            }
        }

        return res;
    }

    private double[]generateNapiv(TriangleL tr,double[] u0){
        double i = 0,
               j = 0,
               k = 0;
        double H = 0,Sigma = 0;
        Node vA = AllNodeList.get(tr.A-1),
             vB = AllNodeList.get(tr.B-1),
             vC = AllNodeList.get(tr.C-1);
        boolean a1=false,a2=false,a3=false;
        for (int index = 0; index < NodeList2.size(); index++) {
            Boundery boundery = NodeList2.get(index);

            i=0;j=0;k=0;

            for (int iter = 0; iter < boundery.getNodeList().size(); iter++) {
                BounderyNodePair temp = boundery.getNodeList().get(iter);

                if(temp.contains(vA, vB)){
                    i=1.0;
                    j=1.0;
                    a1=true;
                }
                if(temp.contains(vA, vC)){
                    i=1.0;
                    k=1.0;
                    a2=true;
                }
                if(temp.contains(vC, vB)){
                    k=1.0;
                    j=1.0;
                    a3=true;
                }

            }
            if(i+j+k==2){
                H = Double.parseDouble(boundery.getParams().h);
                double length = i*j*( Math.sqrt( (vA.x - vB.x)*(vA.x - vB.x) + (vA.y - vB.y)*(vA.y - vB.y) ) ) +
                                k*j*( Math.sqrt( (vC.x - vB.x)*(vC.x - vB.x) + (vC.y - vB.y)*(vC.y - vB.y) ) ) +
                                i*k*( Math.sqrt( (vA.x - vC.x)*(vA.x - vC.x) + (vA.y - vC.y)*(vA.y - vC.y) ) );
                length/=H;

                System.out.println("зайшли 1");
                System.out.println(i*2*length*u0[tr.A-1] + i*j*length*u0[tr.B-1] + i*k*length*u0[tr.C-1]+"");
                System.out.println(i*j*length*u0[tr.A-1] + j*2*length*u0[tr.B-1] + j*k*length*u0[tr.C-1]+"");
                System.out.println(i*k*length*u0[tr.A-1] + k*j*length*u0[tr.B-1] + k*2*length*u0[tr.C-1]+"");

                return new double[]{
                     i*2*length*u0[tr.A-1] + i*j*length*u0[tr.B-1] + i*k*length*u0[tr.C-1],
                     i*j*length*u0[tr.A-1] + j*2*length*u0[tr.B-1] + j*k*length*u0[tr.C-1],
                     i*k*length*u0[tr.A-1] + k*j*length*u0[tr.B-1] + k*2*length*u0[tr.C-1]
                };
            }else{
                if(i+j+k>2){
                    System.out.println("зайшли 2");
                    H = Double.parseDouble(boundery.getParams().h);
                    double[] res = new  double[3];
                    if(a1){
                        double length = ( Math.sqrt( (vA.x - vB.x)*(vA.x - vB.x) + (vA.y - vB.y)*(vA.y - vB.y) ) );
                        length*=H;
                        res[0] = 2*length*u0[tr.A-1] + length*u0[tr.B-1];
                        res[1] = length*u0[tr.A-1] + 2*length*u0[tr.B-1];
                    }
                    if(a2){
                        double length = ( Math.sqrt( (vA.x - vC.x)*(vA.x - vC.x) + (vA.y - vC.y)*(vA.y - vC.y) ) );
                        length*=H;
                        res[0]=2*length*u0[tr.A-1] + length*u0[tr.C-1];
                        res[2]=length*u0[tr.A-1] + 2*length*u0[tr.C-1];
                    }
                    if(a3){
                        double length = ( Math.sqrt( (vC.x - vB.x)*(vC.x - vB.x) + (vC.y - vB.y)*(vC.y - vB.y) ) );
                        length*=H;
                        res[1]=2*length*u0[tr.B-1] + length*u0[tr.C-1];
                        res[2]=length*u0[tr.B-1] + 2*length*u0[tr.C-1];
                    }

                    return res;
                }
            }
        }
        return new double[]{0.0 , 0.0 , 0.0};

    }
}
